package br.ufrn.cerescaico.sepe.dao;

import br.ufrn.cerescaico.sepe.Sepe;
import br.ufrn.cerescaico.sepe.beans.Bean;
import br.ufrn.cerescaico.sepe.dao.util.DataAccessLayerException;
import br.ufrn.cerescaico.sepe.dao.util.HibernateFactory;
import java.util.List;

import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;

/**
 * Classe abstrata topo da hierarquia de DAO's (Data Access Object).
 * @author Taciano Morais Silva
 * @version 12/02/2009, 11h28m
 * @since 15/10/2008, 12h00m
 */
public abstract class AbstractDao<T extends Bean> {

    /**
     * Constante que guarda o índice do primeiro registro
     * retornado em uma consulta.
     */
    protected static int PRIMEIRO_REGISTRO = 0;

    protected Session session;
    protected Transaction tx;

    /**
     * Fachada do Sistema
     */
    private Sepe sepe;

    /**
     * Nome do banco de dados
     */
    private String banco;

    /**
     * Construtor do DAO que inicia a Factory do hibernate se necessário.
     * @throws DataAccessLayerException Quando ocorrer uma falha no acesso BD.
     */
    public AbstractDao() throws DataAccessLayerException {
        HibernateFactory.buildIfNeeded();
    }

    /**
     * @throws DataAccessLayerException Quando ocorrer uma falha no acesso BD.
     */
    public AbstractDao(String banco) throws DataAccessLayerException {
        this.banco = banco;
        HibernateFactory.buildIfNeeded(banco);
    }

    /**
     * @param sepe
     * @throws DataAccessLayerException Quando ocorrer uma falha no acesso BD.
     */
    public AbstractDao(Sepe sepe) throws DataAccessLayerException {
        this();
        this.sepe = sepe;
    }

    /**
     * @param sepe
     * @throws DataAccessLayerException
     */
    public AbstractDao(Sepe sepe, String banco) throws DataAccessLayerException {
        this(banco);
        this.sepe = sepe;
    }

    /**
     * Salva ou atualiza o objeto no Banco de Dados
     * @param obj Objeto a salvar/atualizar.
     * @throws DataAccessLayerException Quando ocorrer uma falha no acesso BD.
     */
    protected void saveOrUpdate(T obj) throws DataAccessLayerException {
        try {

            startOperation();
            session.saveOrUpdate(obj);
            tx.commit();
        } catch (HibernateException e) {
            handleException(e);
        } finally {
            HibernateFactory.close(session);
        }
    }

    /**
     * @param obj
     * @throws DataAccessLayerException
     */
    @SuppressWarnings("unchecked")
    protected void delete(T obj) throws DataAccessLayerException {
        try {
            startOperation();
            obj = (T)session.load(obj.getClass(), obj.getId());
            session.delete(obj);
            tx.commit();
        } catch (HibernateException e) {
            handleException(e);
        } finally {
            HibernateFactory.close(session);
        }
    }

    /**
     * @param clazz
     * @param id
     * @throws DataAccessLayerException
     */
    protected void delete(Class<T> clazz, Integer id) throws DataAccessLayerException {
        Object obj = null;
        try {
            startOperation();
            obj = session.load(clazz, id);
            session.delete(obj);
            tx.commit();
        } catch (HibernateException e) {
            handleException(e);
        } finally {
            HibernateFactory.close(session);
        }
    }

    /**
     * @param clazz
     * @param id
     * @return
     * @throws DataAccessLayerException
     */
    protected Object find(Class<T> clazz, Integer id) throws DataAccessLayerException {
        Object obj = null;
        try {
            startOperation();
            obj = session.load(clazz, id);
            tx.commit();
        } catch (HibernateException e) {
            handleException(e);
        } finally {
            //HibernateFactory.close(session);
            //if (sepe != null) {
                sepe.addSessaoHibernate(session);
            //}
        }
        return obj;
    }

    /**
     * @param clazz
     * @return
     * @throws DataAccessLayerException
     */
    @SuppressWarnings("unchecked")
    protected List<T> findAll(Class<T> clazz) throws DataAccessLayerException {
        List<T> objects = null;
        try {
            startOperation();
            Query query = session.createQuery("from " + clazz.getName());
            objects = query.list();
            tx.commit();
        } catch (HibernateException e) {
            handleException(e);
        } finally {
            HibernateFactory.close(session);
        }
        return objects;
    }

    /**
     * @throws HibernateException
     */
    protected void startOperation() throws HibernateException {
        session = HibernateFactory.openSession();
        tx = session.beginTransaction();
    }

    /**
     * @param e
     * @throws DataAccessLayerException
     */
    protected void handleException(HibernateException e) throws DataAccessLayerException {
        HibernateFactory.rollback(tx);
        throw new DataAccessLayerException(e);
    }
}
